﻿using System.Diagnostics;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Audio;

namespace ADiKt {
    /// <summary>
    /// Manages 1+ GameScreen(s). It maintains a stack of screens, calls their Update and Draw
    /// methods at the appropriate times, and routes input to the topmost active screen.
    /// </summary>

    public class ScreenManager : DrawableGameComponent {
        List<Screen> screens = new List<Screen>();
        List<Screen> screensToUpdate = new List<Screen>();
        InputState input = new InputState();
        bool isInitialized;
        //bool traceEnabled;

        //shared content
        SpriteBatch spriteBatch;
        SpriteFont font;
        SpriteFont scoreFont;
        Texture2D blankTexture;

        SoundEffect menuChangeSound;
        SoundEffect menuSelectSound;
        SoundEffect menuSpinSound;

        // A default SpriteBatch shared by all the screens.
        public SpriteBatch SpriteBatch {
            get { return spriteBatch; }
        }

        // A default font shared by all the screens.
        public SpriteFont Font {
            get { return font; }
        }

        public SpriteFont smallFont {
            get { return scoreFont; }
        }

        public SoundEffect MenuChangeSound {
            get { return menuChangeSound; }
        }

        public SoundEffect MenuSelectSound {
            get { return menuSelectSound; }
        }

        public SoundEffect MenuSpinSound {
            get { return menuSpinSound; }
        }

        /// <summary>
        /// If true, the manager prints out a list of all the screens
        /// each time it is updated. This can be useful for making sure
        /// everything is being added and removed at the right times.
        /// </summary>
        //public bool TraceEnabled {
        //    get { return traceEnabled; }
        //    set { traceEnabled = value; }
        //}

        // Constructs a new screen manager component.
        public ScreenManager(Game game) : base(game) {}

        // Initializes the screen manager component.
        public override void Initialize() {
            base.Initialize();
            isInitialized = true;
        }

        // Load your graphics content.
        protected override void LoadContent() {
            // Load content belonging to the screen manager.
            ContentManager content = Game.Content;

            spriteBatch = new SpriteBatch(GraphicsDevice);
            font = content.Load<SpriteFont>("menufont");
            scoreFont = content.Load<SpriteFont>("scorefont");
            blankTexture = content.Load<Texture2D>("blank");
            menuChangeSound = content.Load<SoundEffect>("menuChange");
            menuSelectSound = content.Load<SoundEffect>("menuSelect");
            menuSpinSound = content.Load<SoundEffect>("menuSpin");

            // Tell each of the screens to load their content.
            foreach (Screen screen in screens) {
                screen.LoadContent();
            }
        }

        // Unload your graphics content.
        protected override void UnloadContent() {
            // Tell each of the screens to unload their content.
            foreach (Screen screen in screens) {
                screen.UnloadContent();
            }
        }

        /// <summary>
        /// Allows each screen to run logic.
        /// </summary>
        public override void Update(GameTime gameTime) {
            // Read the keyboard
            input.Update();
       
            // Make a copy of the master screen list, to avoid confusion if
            // the process of updating one screen adds or removes others.
            screensToUpdate.Clear();

            foreach (Screen screen in screens)
                screensToUpdate.Add(screen);

            bool otherScreenHasFocus = !Game.IsActive;
            bool coveredByOtherScreen = false;

            // Loop as long as there are screens waiting to be updated.
            while (screensToUpdate.Count > 0) {
                // Pop the topmost screen off the waiting list.
                Screen screen = screensToUpdate[screensToUpdate.Count - 1];

                screensToUpdate.RemoveAt(screensToUpdate.Count - 1);

                // Update the screen.
                screen.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);

                if (screen.ScreenState == ScreenState.TransitionOn || screen.ScreenState == ScreenState.Active) {
                    // If first active screen, handle input.
                    if (!otherScreenHasFocus) {
                        screen.HandleInput(input);
                        otherScreenHasFocus = true;
                    }

                    // If this is an active non-popup, inform any subsequent
                    // screens that they are covered by it.
                    if (!screen.IsPopup)
                        coveredByOtherScreen = true;
                }
            }

        }

        // Prints a list of all the screens, for debugging.
        void TraceScreens() {
            List<string> screenNames = new List<string>();
            foreach (Screen screen in screens)
                screenNames.Add(screen.GetType().Name);
            System.Console.WriteLine(string.Join(", ", screenNames.ToArray()));
        }


        // Tells each screen to draw itself.
        public override void Draw(GameTime gameTime) {
            foreach (Screen screen in screens) {
                if (screen.ScreenState == ScreenState.Hidden)
                    continue;

                screen.Draw(gameTime);
            }
        }

        // Adds a new screen to the screen manager.
        public void AddScreen(Screen screen, PlayerIndex? controllingPlayer) {
            screen.ControllingPlayer = controllingPlayer;
            screen.ScreenManager = this;
            screen.IsExiting = false;

            // If we have a graphics device, tell the screen to load content.
            if (isInitialized) {
                screen.LoadContent();
            }

            screens.Add(screen);
            //TraceScreens();
        }


        /// <summary>
        /// Removes a screen from the screen manager. You should normally
        /// use GameScreen.ExitScreen instead of calling this directly, so
        /// the screen can gradually transition off rather than just being
        /// instantly removed.
        /// </summary>
        public void RemoveScreen(Screen screen) {
            // If we have a graphics device, tell the screen to unload content.
            if (isInitialized) {
                screen.UnloadContent();
            }

            screens.Remove(screen);
            screensToUpdate.Remove(screen);
            TraceScreens();
        }


        /// <summary>
        /// Expose an array holding all the screens. We return a copy rather
        /// than the real master list, because screens should only ever be added
        /// or removed using the AddScreen and RemoveScreen methods.
        /// </summary>
        public Screen[] GetScreens() {
            return screens.ToArray();
        }


        // Helper draws a translucent black fullscreen sprite, used for fading
        // screens in and out, and for darkening the background behind popups.
        public void FadeBackBufferToBlack(float alpha) {
            Viewport viewport = GraphicsDevice.Viewport;
            spriteBatch.Begin();
            spriteBatch.Draw(blankTexture, new Rectangle(0, 0, viewport.Width, viewport.Height), Color.Black * alpha);
            spriteBatch.End();
        }

    }
}
